import { getChunkFile, saveChunkFile, uploadChunkFile, mergeChunkFile } from '@api/api.js'
import SparkMD5 from 'spark-md5' // 根据文件内容获取hash值，作为文件的唯一标识
// import qs from 'qs'
import { cloneDeep } from 'lodash'

let _fileList = []
let _previewInfo = {}
const _uploadLimit = 3
let _callback = null
// let _hashWorker = null

// 根据文件大小调整分片的大小，单位MB
function getChunkSize(size) {
  const MB = 1024 * 1024 * 1
  const fileSize = size / MB
  if (fileSize > 1024) {
    return 20 * MB
  } else if (fileSize > 512) {
    return 10 * MB
  } else if (fileSize > 64) {
    return 5 * MB
  } else if (fileSize > 10) {
    return 2 * MB
  } else {
    return 1 * MB
  }
}

// 创建切片
function createFileChunk(file, chunkSize) {
  const fileChunks = [] // 分片的文件
  // 文件开始分片，push到fileChunkedList数组中
  for (let i = 0; i < file.size; i = i + chunkSize) {
    const tmp = file.slice(i, Math.min(i + chunkSize, file.size))
    fileChunks.push(tmp)
  }
  return fileChunks
}

// 获取文件hash
function createFileHash(chunks) {
  const spark = new SparkMD5.ArrayBuffer() // md5的ArrayBuffer加密类
  const step = chunks.length > 10 ? Math.ceil(chunks.length / 10) : 1
  const chunkList = []
  for (let i = 0; i < chunks.length; i += step) {
    chunkList.push(chunks[i])
  }

  return new Promise(resolve => {
    const createMd5 = index => {
      const fileReader = new FileReader() // 文件读取类
      fileReader.readAsArrayBuffer(chunkList[index])
      fileReader.onload = e => {
        spark.append(e.target.result)
        if (index === chunkList.length - 1) {
          const md5 = spark.end()
          console.log('文件md5为：', md5)
          resolve(md5)
        } else {
          createMd5(index + 1)
        }
      }
    }
    createMd5(0)
  })
}

// 暂停上传
function pauseUpload(file) {
  if (_previewInfo) {
    _previewInfo[file.md5].isStop = !_previewInfo[file.md5].isStop
    file.isStop = !file.isStop
    for (let i = 0; i < _fileList.length; i++) {
      const _file = _fileList[i]
      if (_file.md5 === file.md5) {
        _file.isStop = file.isStop
        if (!_file.isStop) {
          sendRequest(_file)
        }
        break
      }
    }
  }
}

// 查询文件的上传状态
function getFileUploadStatus(hash) {
  return new Promise(resolve => {
    getChunkFile({ oneParam: hash })
      .then(res => {
        const noUploadIndex = []
        const uploadIndex = []
        let uploadSize = 0
        if (res.status === 200) {
          const { itemList = [], status = '0', fileShowVO = {} } = res.data || {}
          if (itemList && itemList.length) {
            itemList.forEach(chunk => {
              if (chunk.status === '1') {
                uploadSize += chunk.chunkSize
                uploadIndex.push(chunk.chunkIndex)
              } else {
                noUploadIndex.push(chunk.chunkIndex)
              }
            })
          }
          resolve({ uploadIndex, noUploadIndex, uploadSize, fileShowVO, uploadRes: !res.data || (res.data && !res.data.status) ? false : status })
        } else {
          resolve({ uploadRes: 'error' })
        }
      })
      .catch(() => {
        resolve({ uploadRes: 'error' })
      })
  })
}

// 合并上传的文件
const mergeFile = async (hash, n, file) => {
  const res = await mergeChunkFile({ oneParam: hash })
  n++
  if (res.status === 200 && res.data) {
    file.status = 'success'
    file.percentage = 100
    file.uploadSize = file.size
    file.fileInfo = res.data || {}
    _callback(file)
  } else if (n < 3) {
    mergeFile(hash, n, file)
  }
}

// 创建上传任务队列
function sendRequest(file) {
  return new Promise((resolve, reject) => {
    if (file.size === file.uploadSize || file.uploadIndex.length === file.chunks.length) {
      file.status = 'success'
      file.chunks.forEach(chunk => {
        chunk.status = 'success'
      })
      file.percentage = 100
      _callback(file)
      resolve(true)
    }
    let limit = _uploadLimit || 3 // limit是上传并发数
    const chunks = cloneDeep(file.chunks.filter(item => item.status !== 'success'))
    const len = chunks.length
    let counter = 0 // 记录上传成功的片数
    const controller = new AbortController()

    const start = () => {
      if (_previewInfo[file.md5].isStop) {
        file.isStop = true
        controller && controller.abort()
        resolve(false)
        return false
      }

      const chunk = chunks.shift()
      if (chunk && chunk.errorCount < 2) {
        uploadChunkFile(chunk.formData, e => {
          if (e.total > 0) {
            const successCount = file.chunks.filter(item => item.status === 'success').length
            const perChunk = 100 / file.chunks.length // 每份切片比重
            const hasSuccess = successCount * perChunk // 已经上传成功的百分比
            e.percent = (e.loaded / e.total) * 100
            if (e.percent < 100) {
              file.chunks[chunk.index].status = 'playing'
            } else {
              let percentage = (hasSuccess + (e.percent * perChunk) / 100).toFixed(2)
              percentage = Number(percentage)
              file.percentage = percentage
              file.uploadSize += chunk.chunkSize
            }
          }
        })
          .then(res => {
            if (res.status === 200) {
              counter++
              file.chunks[chunk.index].status = 'success'
              if (counter === len) {
                // 最后一个上传完成
                file.status = 'success'
                file.percentage = 99
                file.uploadSize = file.size
                resolve(true)
                // 合并文件
                mergeFile(file.md5, 0, file)
              } else {
                start()
              }
            } else {
              chunk.errorCount++
              file.chunks[chunk.index].status = 'error'
              chunks.unshift(chunk)
              start()
            }
            _callback(file)
          })
          .catch(err => {
            file.chunks[chunk.index].status = 'error'
            file.isStop = true
            _callback(file)
            reject(err)
          })
      } else {
        resolve(false)
      }
    }

    // 并发数
    while (limit > 0) {
      setTimeout(() => {
        start()
      }, Math.random() * 1000)
      limit -= 1
    }
  })
}

// 对每个文件进行分片及hash处理
const chunkFiles = async file => {
  const { size = 0 } = file
  const chunkSize = getChunkSize(size) // 分片大小

  let fileChunkedList = createFileChunk(file, chunkSize) // 文件分片完成之后的数组
  const md5 = await createFileHash(fileChunkedList) // 读取第一片分片，获取文件hash值

  if (md5) {
    file.md5 = md5
    _previewInfo[md5] = {
      isStop: false,
      status: 'ready',
    }
  }

  const { uploadIndex = [], uploadSize = 0, fileShowVO = {}, uploadRes } = await getFileUploadStatus(md5)
  if (uploadRes === 'error') {
    _callback({
      name: file.name,
      size: file.size,
      md5: file.md5 || '',
      status: 'error',
      isStop: true,
      percentage: 0,
      uploadSize: uploadSize,
      chunks: [],
      uploadIndex: uploadIndex,
      fileInfo: {},
    })
    return false
  } else if (uploadRes === '1') {
    _callback({
      name: file.name,
      size: file.size,
      md5: file.md5 || '',
      status: 'playing',
      isStop: false,
      percentage: 99,
      uploadSize: file.size - 1,
      chunks: [],
      uploadIndex: uploadIndex,
      fileInfo: fileShowVO,
    })
    setTimeout(() => {
      _callback({
        name: file.name,
        size: file.size,
        md5: file.md5 || '',
        status: 'success',
        isStop: false,
        percentage: 100,
        uploadSize: file.size,
        chunks: [],
        uploadIndex: uploadIndex,
        fileInfo: fileShowVO,
      })
    }, 1000)
    return false
  }

  fileChunkedList = fileChunkedList.map((item, index) => {
    const formData = new FormData()
    // 这些字段看后端需要哪些，就传哪些，也可以自己追加额外参数
    formData.append('file', item) // 文件
    formData.append('chunk', index)
    // formData.append('chunkNumber', index + 1) // 当前文件块
    // formData.append('chunkSize', chunkSize) // 单个分块大小
    // formData.append('currentChunkSize', item.size) // 当前分块大小
    // formData.append('totalSize', file.size) // 文件总大小
    formData.append('fileMd5', md5) // 文件标识
    // formData.append('filename', file.name) // 文件名
    // formData.append('totalChunks', fileChunkedList.length) // 总块数
    return {
      formData: formData,
      index: index,
      status: uploadIndex.includes(index) ? 'success' : 'ready',
      md5: md5,
      filename: file.name,
      errorCount: 0,
      chunkSize: item.size,
    }
  })

  file.chunks = fileChunkedList

  // 保存文件的分片信息
  const _chunks = []
  fileChunkedList.forEach(chunk => {
    _chunks.push({
      chunkIndex: chunk.index,
      chunkSize: chunk.chunkSize,
    })
  })

  if (!uploadRes && typeof uploadRes === 'boolean') {
    const _params = {
      chunkTotal: file.chunks.length,
      fileMd5: file.md5,
      fileName: file.name,
      fileSize: Math.ceil(file.size / 1024),
      itemList: _chunks,
    }
    await saveChunkFile(_params)
  }

  _fileList.push({
    name: file.name,
    size: file.size,
    md5: file.md5 || '',
    status: file.status || 'ready',
    isStop: file.isStop || false,
    percentage: file.percentage || 0,
    uploadSize: uploadSize,
    chunks: fileChunkedList,
    uploadIndex: uploadIndex,
  })
}

// 分片上传文件
const chunkFileUpload = async (files, callback) => {
  _fileList = []
  _previewInfo = {}
  _callback = callback
  for (let i = 0; i < files.length; i++) {
    await chunkFiles(files[i])
  }

  // 发起请求
  for (let j = 0; j < _fileList.length; j++) {
    const result = await sendRequest(_fileList[j])
    if (typeof result === 'boolean' && result) {
      continue
    } else {
      break
    }
  }
}

export { pauseUpload }

export default chunkFileUpload
